#include "os_cfg.h"

	.text
	.extern kernel_init
	.global _start 
	# void start(boot_info_t *boot_info)
_start:
	# 把start函数的参数取出来并传递给kernel_init
	mov 4(%esp), %eax
	push %eax
	# kernel_init(boot_info)
	call kernel_init

	# 远跳转实现选择子的重新加载
	jmp $KERNEL_SELECTOR_CS, $gdt_reload
gdt_reload:
	mov $KERNEL_SELECTOR_DS, %ax			# 16表示内容数据段选择子
	mov %ax, %ds
	mov %ax, %ss
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs

	mov $(stack + KERNEL_STACK_SIZE), %esp
	jmp init_main

# 声明一个临时栈段(只在内核初始化的时候使用)
	.bss
.comm stack, KERNEL_STACK_SIZE
 
# 在汇编中定义宏并包含参数：函数名、异常向量号、是否有错误号
.macro exception_handler name num with_err_code
	.text
	.extern do_handler_\name
	.global exception_handler_\name
exception_handler_\name:
	.if \with_err_code == 0
		push $0
	.endif

	push $\num

	pusha
	push %ds
	push %es
	push %fs
	push %gs
	
	# 压入do_handler_\name函数参数frame
	push %esp
	call do_handler_\name;
	pop %esp

	pop %gs
	pop %fs
	pop %es
	pop %ds
	popa

	# 这里是把错误码和中断向量num出栈（注意单异常本身有错误码的时候CPU不会自动把错误码出栈，需要我们手动出栈）
	add $8, %esp

	iret
.endm

exception_handler unknown, -1, 0
exception_handler divider, 0, 0
exception_handler Debug, 1, 0
exception_handler NMI, 2, 0
exception_handler breakpoint, 3, 0
exception_handler overflow, 4, 0
exception_handler bound_range, 5, 0
exception_handler invalid_opcode, 6, 0
exception_handler device_unavailable, 7, 0
exception_handler double_fault, 8, 1
exception_handler invalid_tss, 10, 1
exception_handler segment_not_present 11, 1
exception_handler stack_segment_fault, 12, 1
exception_handler general_protection, 13, 1
exception_handler page_fault, 14, 1
exception_handler fpu_error, 16, 0
exception_handler alignment_check, 17, 1
exception_handler machine_check, 18, 0
exception_handler simd_exception, 19, 0
exception_handler virtual_exception, 20, 0
exception_handler control_exception, 21, 1
exception_handler time, 0x20, 0
exception_handler kbd, 0x21, 0 
exception_handler ide_primary, 0x2E, 0

	# simple_switch(&from, to)
	.text
	.global simple_switch
simple_switch:
	mov 4(%esp), %eax
	mov 8(%esp), %edx

	push %ebp
	push %ebx
	push %esi
	push %edi

	# 把当前任务的栈指针存放到当前任务的task结构中的stack字段中
	mov %esp, (%eax)		# %eax 存放的是stack的地址
	# 把esp的值设置为目标任务的栈顶指针值
	mov %edx, %esp
	pop %edi
	pop %esi
	pop %ebx
	pop %ebp

	ret

	.global exception_handler_syscall
	.extern do_handler_syscall
exception_handler_syscall:
	pusha
	push %ds
	push %es
	push %fs
	push %gs
	pushf
	
	mov %esp, %eax
	push %eax			# do_handler_syscall的参数frame
	# void do_handler_syscall(syscall_frame_t *frame)
	call do_handler_syscall
	add $4, %esp
	
	popf
	pop %gs
	pop %fs
	pop %es
	pop %ds
	popa

	retf $(5 * 4)	# 远返回依次从栈中弹出CS、EIP并把它们放到对应的寄存器里面，并把esp的值+=（5*4）也就是跳过我们传入的参数
					# 此时会从0特权级切换回3特权级
					# ret返回的话不会切换CS、EIP，影响特权级别切换
